#include "master.h"

#include "sysGameLoop.h"
#include "sysConsole.h"
#include "sysEditor.h"

#include "gameSystem.h"

#include "debugVars.h"

#include "gameMain.h"

#include "entCube.h"

#include "ofx3DModelLoader.h"

#include "types.h"

//--------------------------------------------------------------------------------------------------

static float rotateX;
static float rotateY;
static float rotateZ;
static int fadeRate = 37;

static float blurAmount = 4.0f;

static bool postProcessing;

//--------------------------------------------------------------------------------------------------

class sysGameLoop : public gameSystem
{
public:
	void setup();
	void update(float timeDelta);
	void draw();
	void windowResized(int w, int h);
	void exit();

	void keyPressed	(int key);
	void keyReleased (int key);

	Level* getLevel();

	void drawScene();

	ofEasyCam* getEditorCamera();
	ofCamera* getCamera();

private:

	Level currentLevel;

	ofCamera  gameCamera;
	ofEasyCam editorCamera;

	ofx3DModelLoader modelLoaderTest;

	// Shaders
	ofShader hBlurShader;
	ofShader vBlurShader;
	ofShader glowShader;

	// Frame buffer objects
	ofFbo hBlurFbo;
	ofFbo vBlurFbo;
};

//--------------------------------------------------------------------------------------------------

static sysGameLoop sysGameLoopInstance;

//--------------------------------------------------------------------------------------------------

void sysGameLoop::setup()
{
	debugVar_register(&rotateX, "rotateX");
	debugVar_register(&rotateY, "rotateY");
	debugVar_register(&rotateZ, "rotateZ");
	debugVar_register(&fadeRate, "fade_rate");
	debugVar_register(&blurAmount, "blur_amount");
	debugVar_register(&postProcessing, "post_processing");

	postProcessing = false;

	sysConsole_logMessage("sysGameLoop initialized!");

	// Register entity types
	registerEntityTypes();

	// Fog
	GLfloat fogColor[4] = {0,0,0,1.0};
	glFogi(GL_FOG_MODE, GL_LINEAR);
	glFogfv(GL_FOG_COLOR, fogColor);
	glFogf(GL_FOG_DENSITY, 1);
	glHint(GL_FOG_HINT, GL_DONT_CARE);
	glFogf(GL_FOG_START, 0);
	glFogf(GL_FOG_END, 2000);
	//glEnable(GL_FOG);

	// Framebuffer object
	hBlurFbo.allocate(ofGetWidth(),ofGetHeight(), GL_RGBA);
	vBlurFbo.allocate(ofGetWidth(),ofGetHeight(), GL_RGBA);

	hBlurFbo.begin();
	ofBackground(ofColor::black);
	hBlurFbo.end();

	vBlurFbo.begin();
	ofBackground(ofColor::black);
	vBlurFbo.end();

	// Shaders
	hBlurShader.load("", "shaders/simpleBlurHorizontal.frag");
	vBlurShader.load("", "shaders/simpleBlurVertical.frag");
	glowShader.load("", "shaders/glow.frag");

	// setup camera
	editorCamera.setFarClip(100000);
	gameCamera.setFov(30.0f);

	// Load level
	currentLevel.load();

	// Test load mesh
	modelLoaderTest.loadModel("models/building01.3ds", 100.0f);
	modelLoaderTest.setRotation(0, 270, 1, 0, 0);
    modelLoaderTest.setRotation(1, 90, 0, 0, 1);
}

//--------------------------------------------------------------------------------------------------

void sysGameLoop::update(float timeDelta)
{
	currentLevel.update(timeDelta);

	// update camera position
	Entity* camera = sysGameLoop_findEntity("Camera");

	if(camera)
		gameCamera.setTransformMatrix(camera->getGlobalTransformMatrix());
}

//--------------------------------------------------------------------------------------------------

void sysGameLoop::drawScene()
{
	if(!postProcessing)
	{
		glEnable(GL_CULL_FACE);
		glEnable(GL_DEPTH_TEST);
	}

	// Temporary test game scene
	if(sysEditor_isEnabled())
		editorCamera.begin();
	else
		gameCamera.begin();

	ofPushMatrix();
	ofRotateX(rotateX);
	ofRotateY(rotateY);
	ofRotateZ(rotateZ);

	currentLevel.draw();
	ofSetColor(255);
	modelLoaderTest.draw();

	ofPopMatrix();
	if(sysEditor_isEnabled())
		editorCamera.end();
	else
		gameCamera.end();

	if(!postProcessing)
	{
		glDisable(GL_CULL_FACE);
		glDisable(GL_DEPTH_TEST);
	}
}

//--------------------------------------------------------------------------------------------------

void sysGameLoop::draw()
{
	//--
	// With regards to this whole function : HERE BE DRAGONS (unless you understand shaders and fbo, then I guess it's not that bad)
	//--
	ofEnableAlphaBlending();

	// Draw base scene
	drawScene();

	if(postProcessing)
	{
		// Draw base scene
		// Begin rendering to hBlurFbo
		hBlurFbo.begin();

		// This is to achieve the trails effect, the frame buffer isn't cleared
		// on each draw frame, and is instead faded out with this black rectangle with alpha
		// Fade rate, fill the hBlurFbo
		ofFill();
		ofSetColor(0,0,70, fadeRate);
		ofRect(0,0,hBlurFbo.getWidth(),hBlurFbo.getHeight());

		// Makes dark things brighter
		glowShader.begin();

		drawScene();

		// End rendering to hBlurFbo
		hBlurFbo.end();

		ofSetColor(255,255,255);

		// Perform blurs
		vBlurFbo.begin();
		hBlurShader.begin();
		hBlurShader.setUniform1f("blurAmnt", blurAmount);
		hBlurFbo.draw(0,0);
		hBlurShader.end();
		vBlurFbo.end();

		// Don't even ask!?!!? :(
		ofPushMatrix();
		ofScale(1.0f, -1.0f);

		vBlurShader.begin();
		vBlurShader.setUniform1f("blurAmnt", blurAmount);
		vBlurFbo.draw(0,-ofGetHeight());
		vBlurShader.end();

		glowShader.end();

		ofPopMatrix();
	}
}

//--------------------------------------------------------------------------------------------------

void sysGameLoop::windowResized(int w, int h)
{
	hBlurFbo.allocate(w,h, GL_RGBA);
	vBlurFbo.allocate(w,h, GL_RGBA);
}

void sysGameLoop::keyPressed(int key)
{

}

void sysGameLoop::keyReleased(int key)
{

}

//--------------------------------------------------------------------------------------------------

void sysGameLoop::exit()
{
	currentLevel.destroy();
}

//--------------------------------------------------------------------------------------------------

Level* sysGameLoop::getLevel()
{
	return &currentLevel;
}

//--------------------------------------------------------------------------------------------------

Level* sysGameLoop_getCurrentLevel()
{
	return sysGameLoopInstance.getLevel();
}

//--------------------------------------------------------------------------------------------------

ofCamera* sysGameLoop::getCamera()
{
	return &gameCamera;
}

//--------------------------------------------------------------------------------------------------

ofEasyCam* sysGameLoop::getEditorCamera()
{
	return &editorCamera;
}

//--------------------------------------------------------------------------------------------------

ofCamera* sysGameLoop_getCamera()
{
	return sysGameLoopInstance.getCamera();
}

//--------------------------------------------------------------------------------------------------

ofEasyCam* sysGameLoop_getEditorCamera()
{
	return sysGameLoopInstance.getEditorCamera();
}

//--------------------------------------------------------------------------------------------------

Entity* sysGameLoop_findEntity(string name)
{
	return sysGameLoop_getCurrentLevel()->getEntity(name);
}

//--------------------------------------------------------------------------------------------------

Entity* sysGameLoop_getEntityUnderCursor(int x, int y)
{
	return sysGameLoopInstance.getLevel()->getEntityUnderCursor(x, y, sysGameLoop_getEditorCamera());
}

//--------------------------------------------------------------------------------------------------

list<Entity*> sysGameLoop_getEntitiesUnderCursorRect(int x1, int y1, int x2, int y2)
{
	return sysGameLoopInstance.getLevel()->getEntitiesUnderCursorRect(x1, y1, x2, y2, sysGameLoop_getEditorCamera());
}

//--------------------------------------------------------------------------------------------------

gameSystem* sysGameLoop_getSystem()
{
	return &sysGameLoopInstance;
}

//--------------------------------------------------------------------------------------------------
